#ifndef _CHECK_POINT_STATE_HPP
#define _CHECK_POINT_STATE_HPP 1

#include <vector>

#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

#include "../messages/HistoryDigest.hpp"
#include "../messages/Digest.hpp"
#include "../messages/NextBatch.hpp"
#include "../messages/CommandBatch.hpp"
#include "../messages/Entry.hpp"
#include "../messages/CertificateEntry.hpp"

#include "../constants.hpp"

#include "OrderedEntry.hpp"

namespace BFT
{
	namespace order
	{
		namespace statemanagement
		{
			class CheckPointState
			{
				public:
				
				CheckPointState(int _clients);
				
				CheckPointState(boost::shared_ptr<char> _bytes, int _bytes_size);
				
				CheckPointState(boost::shared_ptr<char> _bytes, int _bytes_size, int offset);
				
				CheckPointState(boost::shared_ptr<CheckPointState> _cps);
				
				boost::shared_ptr<char> getBytes(int& _bytes_size);
				
				int getSize();
				
				uint32_t computeSize(); //introduced only to implement the first version of view change
				
				boost::shared_ptr<BFT::messages::HistoryDigest> getHistory(); //inlined
				
				uint32_t getBaseSequenceNumber(); //inlined
				
				uint32_t getCurrentTime(); //inlined
				
				void setCurrentTime(uint32_t _t);
				
				uint32_t getCurrentSequenceNumber(); //inlined
				
				uint32_t getLastOrdered(uint32_t _client); //inlined
				
				uint32_t getLastOrderedSeqNo(uint32_t _client); //inlined				
				
				void addExecCPToken(boost::shared_ptr<char> _cp, int _cp_size, uint32_t _seqNo);
				
				boost::shared_ptr<char> getExecCPToken(int &_size);
				
				void addNextBatch(boost::shared_ptr<BFT::messages::CommandBatch> _cb, uint32_t _seqNo,
						  boost::shared_ptr<BFT::messages::HistoryDigest> _hd);
						  
				void addNextBatch(boost::shared_ptr<BFT::messages::CertificateEntry> _cert, uint32_t _seqNo);
				
				void addNextBatch(boost::shared_ptr<BFT::messages::NextBatch> _nb);
						  
				
				uint32_t getRetransmitDelay(uint32_t _client); //inlined
				
				void updateRetransmitDelay(uint32_t _client); //inlined
				
				bool hasExecCP(); //inlined
				
				boost::shared_ptr<BFT::messages::Digest> getDigest(); //inlined
				
				bool isMarkingStable(); //inlined
				
				void markingStable();
				
				uint32_t getEntryCount(); //inlined
				
				uint32_t getRetransDelayCount(); //inlined
				
				boost::shared_ptr<OrderedEntry> getEntry(int _index); //inlined
				
				uint32_t getRetransDelay(int _index); //inlined
				
				void finalize(); //inlined
				
				void printInfo();  
				
				void makeStable(); //sync
				
				void commit(); //sync
				
				bool isStable(); //sync
				
				bool isCommitted(); //sync
				
				boost::shared_ptr<BFT::messages::Digest> getStableDigest(); //sync
				
				protected:
				
				std::vector<boost::shared_ptr< OrderedEntry > > orderedRequestCache;
				std::vector<uint32_t> retransDelay;
				boost::shared_ptr<char> execCPToken;
				int token_size;
				uint32_t baseSeqNo; //first seqNo in this checkpoint
				uint32_t currentSeqNo;//next seqNo to be executed in this checkpoint.
						      //if execCPToken is not null, then it corresponds
						      //to executing everything prior to this seqNo
				boost::shared_ptr<BFT::messages::HistoryDigest> history; //should always reflect the history up to and including
								      //currentSeqNo -1
				uint32_t currentTime; //current time
				bool committed;
				bool marking;
				boost::shared_ptr<BFT::messages::Digest> stableDigest;	
				
				
				private:
				
				uint32_t baseDelay; 
				
				boost::recursive_mutex condition_mutex;
				boost::condition_variable_any stable_condition;
				
			}; //class CheckPointState
			
			inline boost::shared_ptr<BFT::messages::HistoryDigest> CheckPointState::getHistory() {return history;}
			inline boost::shared_ptr<OrderedEntry> CheckPointState::getEntry(int _index) {return orderedRequestCache.at(_index);}
			inline uint32_t CheckPointState::getBaseSequenceNumber() {return baseSeqNo;}
			inline uint32_t CheckPointState::getCurrentTime() {return currentTime;}
			inline uint32_t CheckPointState::getCurrentSequenceNumber() {return currentSeqNo;}
			inline uint32_t CheckPointState::getLastOrdered(uint32_t _client) {return orderedRequestCache.at(_client).get()->getReqId();}			
			inline uint32_t CheckPointState::getLastOrderedSeqNo(uint32_t _client) {return orderedRequestCache.at(_client).get()->getSeqNo();}
			inline uint32_t CheckPointState::getRetransmitDelay(uint32_t _client) {return retransDelay.at(_client);}
			inline uint32_t CheckPointState::getEntryCount() {return orderedRequestCache.size();}
			inline uint32_t CheckPointState::getRetransDelayCount() {return retransDelay.size();}
			inline uint32_t CheckPointState::getRetransDelay(int _index) {return retransDelay.at(_index);}
			inline void CheckPointState::updateRetransmitDelay(uint32_t _client) {retransDelay.at(_client)*=2;}
			inline void CheckPointState::finalize() {fprintf(stderr,"NIY\n");}
			inline bool CheckPointState::hasExecCP() {return execCPToken.use_count()!=0;}
			inline bool CheckPointState::isMarkingStable() {return marking;}
						
			
			
		}//namespace statemanagement
	}//namespace order
}//namespace BFT

#endif
